package com.etcfg.etlaunch;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import com.etcfg.etlaunch.ApplicationSetting.SlacMode;

public class ETLauncher implements Runnable {
	private ApplicationSetting applicationSetting;
	private ETServer currentServer;
	private Thread thread;
	private Process gameProcess;
	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
			this);
	private boolean waitUntilFinished = true;
	private boolean forceSlac = false;

	public void addPropertyChangeListener(PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(listener);
	}

	public void addPropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
	}

	public void fireIndexedPropertyChange(String propertyName, int index,
			boolean oldValue, boolean newValue) {
		propertyChangeSupport.fireIndexedPropertyChange(propertyName, index,
				oldValue, newValue);
	}

	public void fireIndexedPropertyChange(String propertyName, int index,
			int oldValue, int newValue) {
		propertyChangeSupport.fireIndexedPropertyChange(propertyName, index,
				oldValue, newValue);
	}

	public void fireIndexedPropertyChange(String propertyName, int index,
			Object oldValue, Object newValue) {
		propertyChangeSupport.fireIndexedPropertyChange(propertyName, index,
				oldValue, newValue);
	}

	public void firePropertyChange(PropertyChangeEvent evt) {
		propertyChangeSupport.firePropertyChange(evt);
	}

	public void firePropertyChange(String propertyName, boolean oldValue,
			boolean newValue) {
		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
				newValue);
	}

	public void firePropertyChange(String propertyName, int oldValue,
			int newValue) {
		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
				newValue);
	}

	public void firePropertyChange(String propertyName, Object oldValue,
			Object newValue) {
		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
				newValue);
	}

	public PropertyChangeListener[] getPropertyChangeListeners() {
		return propertyChangeSupport.getPropertyChangeListeners();
	}

	public PropertyChangeListener[] getPropertyChangeListeners(
			String propertyName) {
		return propertyChangeSupport.getPropertyChangeListeners(propertyName);
	}

	public boolean hasListeners(String propertyName) {
		return propertyChangeSupport.hasListeners(propertyName);
	}

	public void removePropertyChangeListener(PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(listener);
	}

	public void removePropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(propertyName,
				listener);
	}

	public ETLauncher(ApplicationSetting applicationSetting) {
		this.applicationSetting = applicationSetting;
	}

	private void launchGame(ETServer etServer, boolean waitUntilFinished)
			throws InterruptedException, IOException {
		File pathToEtExe = applicationSetting.getPathToEtExe();
		gameProcess = Runtime.getRuntime().exec(
				pathToEtExe.getAbsolutePath()
						+ buildParamStringForServer(etServer), null,
				pathToEtExe.getParentFile());
		if (waitUntilFinished) {
			gameProcess.waitFor();
		}
	}

	private void launchGameWithSLAC(ETServer etServer, boolean waitUntilFinished)
			throws InterruptedException, IOException {
		gameProcess = Runtime.getRuntime().exec(
				applicationSetting.getPathToSlac().getAbsolutePath() + " ET "
						+ buildParamStringForServer(etServer));

		gameProcess.waitFor();

	}

	public String buildParamStringForServer(ETServer etServer) {
		ServerStatus serverStatus = etServer.getServerStatus();
		if (serverStatus == null || serverStatus.getPing() == 999) {
			if (etServer.getLastServerStatus() != null) {
				serverStatus = etServer.getLastServerStatus();
			}
		}
		StringBuilder params = new StringBuilder();
		params.append(" +connect ");
		params.append(etServer.getIp());
		params.append(":");
		params.append(etServer.getPort());
		if (serverStatus != null) {
			if (serverStatus.isPunkbusterEnabled()) {
				params.append(" +set cl_punkbuster 1 ");
			}

			if (serverStatus.getMod() != null) {
				params.append(" +set fs_game ");
				params.append(serverStatus.getMod());
			}
		}

		if (etServer.getPassword() != null
				&& etServer.getPassword().isEmpty() == false) {
			params.append(" +set password ");
			params.append(etServer.getPassword());
		}

		if (etServer.getAdditionalParametes().size() > 0) {
			Map<String, String> additionalParametes = etServer
					.getAdditionalParametes();
			Set<Entry<String, String>> entrySet = additionalParametes
					.entrySet();
			for (Entry<String, String> entry : entrySet) {
				params.append(" +set ");
				params.append(entry.getKey());
				params.append(" ");
				params.append(entry.getValue());
			}
		}

		return params.toString();
	}

	@Override
	public void run() {
		propertyChangeSupport.firePropertyChange("gameState", false, true);
		ServerStatus serverStatus = currentServer.getServerStatus();
		if (serverStatus == null || serverStatus.getPing() == 999) {
			serverStatus = currentServer.getLastServerStatus();
		}
		try {
			File pathToRInput = applicationSetting.getPathToRInput();
			if (pathToRInput != null && pathToRInput.exists()) {
				Runtime.getRuntime()
						.exec(
								pathToRInput.getAbsolutePath()
										+ " "
										+ applicationSetting.getPathToEtExe()
												.getName());
			}
			SlacMode slacMode = applicationSetting.getSlacMode();
			if ((serverStatus != null && slacMode.equals(SlacMode.AUTOMATIC)
					&& "etpro".equals(serverStatus.getMod()) && !serverStatus
					.isPunkbusterEnabled())
					|| slacMode.equals(SlacMode.ALWAYS_ON) || forceSlac) {
				if (forceSlac) {
					forceSlac = false;
				}
				launchGameWithSLAC(currentServer, waitUntilFinished);
				return;
			}
			launchGame(currentServer, waitUntilFinished);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		propertyChangeSupport.firePropertyChange("gameState", true, false);
	}

	public boolean startGame(ETServer etServer) {

		if (thread == null || !thread.isAlive()) {
			currentServer = etServer;
			thread = new Thread(this);
			thread.start();
			return true;
		}
		return false;
	}

	public boolean startGameBlocking(ETServer etServer) {
		currentServer = etServer;
		run();
		return false;
	}

	public boolean stopGame() {
		if (thread != null && !thread.isInterrupted()) {
			thread.interrupt();
			gameProcess.destroy();
			try {
				thread.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		}
		return false;
	}

	public boolean isGameRunning() {
		return thread != null && thread.isAlive();
	}

	public boolean isWaitUntilFinished() {
		return waitUntilFinished;
	}

	public void setWaitUntilFinished(boolean waitUntilFinished) {
		this.waitUntilFinished = waitUntilFinished;
	}

	public boolean isForceSlac() {
		return forceSlac;
	}

	public void setForceSlac(boolean forceSlac) {
		this.forceSlac = forceSlac;
	}

}
